/* 
 * Method Ports Library
 *
 *   - a library which offers support for simple Transaction Level Modeling
 *   - see example code for usage guidelines
 *
 */

#ifndef _METHOD_PORTS
#define _METHOD_PORTS

#include <iostream>
#include <string>
#include <vector>

using namespace std;

// Auxiliary abstract classes
template<typename PAYLOAD>
struct method_port_base {
	virtual void transfer(PAYLOAD &p) = 0;
	virtual ~method_port_base();
};

template<typename PAYLOAD>
struct in_method_port_base: method_port_base<PAYLOAD> {
};

template<typename PAYLOAD>
struct out_method_port_base: method_port_base<PAYLOAD> {
};

/******************************************************
 * in_method_port - to be instantiated in a consumer
 *				  - used for one-to-one connections
 ******************************************************/
template<typename MODULE, typename PAYLOAD>
struct in_method_port: in_method_port_base<PAYLOAD> {

	// Bind the in-method port to its implementation
	void register_transfer(MODULE* mod, void (MODULE::*t_fp)(PAYLOAD&)) {
		if (mod == NULL)
			cout << "Warning : [in_method_port : " << name
					<< ".register_transfer(...)] - registering a NULL consumer. Transfers won't work."
					<< endl;
		if (t_fp == NULL)
			cout << "Warning : [in_method_port : " << name
					<< ".register_transfer(...)] - registering a NULL implementation. Transfers won't work."
					<< endl;

		transfer_fp = t_fp;
		target = mod;
	}
	// Constructors
	in_method_port() {
		transfer_fp = NULL;
		target = NULL;
		name = "";
	}
	in_method_port(const char* nm) {
		transfer_fp = NULL;
		target = NULL;
		name = nm;
	}
	// Destructor
	virtual ~in_method_port();
private:
	void (MODULE::*transfer_fp)(PAYLOAD&);
	MODULE* target;
	string name;
	// Called automatically when the in-port receives a payload
	virtual void transfer(PAYLOAD& p) {
		if (target == NULL || transfer_fp == NULL) {
			cout << "Warning : [in_method_port : " << name << ".transfer(...)]"
					<< endl << "    In-method port is not connected properly."
					<< endl << "    Transfer did not take place." << endl
					<< "    Make sure you called register_transfer(...) with the right arguments."
					<< endl;
			return;
		}
		(target->*transfer_fp)(p);
	}

};

/****************************************************
 * out_method_port - to be instantiated in a producer
 *				   - used for one-to-one connections
 ****************************************************/
template<typename MODULE, typename PAYLOAD>
struct out_method_port: out_method_port_base<PAYLOAD> {
	// Binds the out-method port to an in-method port
	void bind(in_method_port_base<PAYLOAD>* o) {
		if (o == NULL)
			cout << "Warning : [out_method_port : " << name
					<< ".bind(...)] - binding to a NULL port. Transfers won't work."
					<< endl;
		other = o;
	}
	// Overloaded operator which does the same thing as bind
	void operator ()(in_method_port_base<PAYLOAD>* o) {
		bind(o);
	}
	// Call this method to transmit a payload to a consumer
	virtual void transfer(PAYLOAD &p) {
		if (other == NULL) {
			cout << "Warning : [out_method_port : " << name << ".tarnsfer(...)]"
					<< endl << "    Out-method port is not connected properly."
					<< endl << "    Transfer did not take place." << endl
					<< "    Make sure you called bind(...) with the right argument."
					<< endl;
			return;
		}
		other->transfer(p);
	}
	// Constructors
	out_method_port() {
		other = NULL;
		name = "";
	}
	out_method_port(const char* nm) {
		other = NULL;
		name = nm;
	}
	// Destructor
	virtual ~out_method_port();
private:
	in_method_port_base<PAYLOAD>* other;
	string name;
};

/****************************************************
 * in_multi_port - to be instantiated in a producer
 * - used for many-to-one connections
 * - works the same way as the in_method_port so no
 * implementation is needed
 ****************************************************/
template<typename MODULE, typename PAYLOAD>
struct in_multi_method_port: in_method_port<MODULE, PAYLOAD> {
	// Constructors

};

/*****************************************************
 * out_multi_port - to be instantiated in a producer
 * - used for one-to-many connections
 ****************************************************/
template<typename MODULE, typename PAYLOAD>
struct out_multi_method_port: out_method_port_base<PAYLOAD> {
	// Binds the out-method port to an in-method port
	// WARNING : be careful not to bind the same port twice
	//			(for performance reasons duplicate ports bindigs are not checked)
	void bind(in_method_port_base<PAYLOAD>* o) {
		if (o == NULL)
			cout << "Warning : [out_method_port : " << name
					<< ".bind(...)] - binding to a NULL port. Transfers won't work."
					<< endl;
		others.push_back(o);
	}
	// Overloaded operator which does the same thing as bind
	void operator ()(in_method_port_base<PAYLOAD>* o) {
		bind(o);
	}
	// Call this method to transmit a payload to all consumers
	virtual void transfer(PAYLOAD &p) {
		for (int i = 0; i < others.size(); i++) {
			if (others[i] == NULL) {
				cout << "Warning : [out_method_port : " << name
						<< ".tarnsfer(...)]" << endl
						<< "    Out-method port is not connected properly."
						<< endl << "    Transfer did not take place." << endl
						<< "    Make sure you called bind(...) with the right argument."
						<< endl;
				return;
			}
			others[i]->transfer(p);
		}
	}
	// Constructors

	// Destructors
	virtual ~out_multi_method_port();
private:
	vector<in_method_port_base<PAYLOAD>*> others;
	string name;
};

#endif

